home *** CD-ROM | disk | FTP | other *** search
/ Aminet 52 / Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso / Aminet / misc / emu / Apex-src.lha / XMODEMA.68K < prev    next >
Text File  |  2001-09-30  |  12KB  |  382 lines

  1. ;XMODEMA.68K    DEC-04-89    (ALSO SEE "INFOSTR")
  2. ;RS-232 handler using X-MODEM protocol
  3. ;by Loren Blaney
  4. ;
  5. ;REVISION HISTORY:
  6. ;MAR-15-86, Original
  7. ;SEP-14-86, Converted to ASM68K conventions, and modified.
  8. ;MAR-09-87, Modified for Amiga's RS-232 interface.
  9. ;JUN-14-88, Changed string termination conventions and fixed small error
  10. ; in baud rate.
  11. ;FEB-15-89, Handle EOTs so it can talk to the rest of the world.
  12. ;DEC-04-89, Modified to allow receiving binary files (EOTFLG), increased
  13. ; timeout to 10 seconds, and don't bomb if previous block is received twice.
  14. ;
  15. ;KNOWN BUGS:
  16. ; A garbled ACK is fatal. This is not as reliable as it should be.
  17. ; Should use serial I/O as a separate device (#4).
  18. ; Should this be a unit instead of a device?
  19. ;
  20. ;NOTES:
  21. ;The bytes in the X-modem format are:
  22. ;    SOH (CTRL-A)
  23. ;    block count
  24. ;    one's complement of the block count
  25. ;    128 bytes of data
  26. ;    checksum of data
  27. ;
  28. ;ACKs and NAKs are used as handshake signals. They are sent by the
  29. ; RECEIVING end, and are a request for a block of data to be sent from
  30. ; the SENDING end. Initially, the RECEIVING end sends NAKs, indicating
  31. ; that it is ready for a block of data. The SENDING end sends the data,
  32. ; and if it is correctly received, an ACK is sent back from the
  33. ; RECEIVING end. If it is incorrectly received, a NAK is sent back, and
  34. ; the SENDING end re-sends the same block of data. When the SENDING end
  35. ; is finished, it sends an EOT.
  36.  
  37.     NOLIST
  38.     INCLUDE    SYSPAG        ;Get the system page difinitions
  39.     LIST
  40.  
  41. DEVNUM    EQU    5        ;Install this handler as device # 5
  42.  
  43. ;ASCII CHARACTERS:
  44. SOH    EQU    $01
  45. EOT    EQU    $04
  46. ACK    EQU    $06
  47. NAK    EQU    $15
  48.  
  49.     ORG    $7EAC0
  50. START    EQU    @        ;Address where this handler starts
  51.  
  52. ;=======================================================================
  53. ;ENTRY POINTS:
  54. ;
  55. XMODEM    DC.L    OPENI        ;$00 = OPENI
  56.     DC.L    OPENO        ;$04 = OPENO
  57.     DC.L    CHIN        ;$08 = CHIN
  58.     DC.L    CHOUT        ;$0C = CHOUT
  59.     DC.L    CLOSE        ;$10 = CLOSE
  60.     DC.L    GETINFO        ;$14 = GETINFO
  61.     DC.L    DUMMY        ;$18 = SPARE
  62.     DC.L    DUMMY        ;$1C = SPARE
  63.  
  64. ;VARIABLES:
  65. INPTR    DC.L    0        ;Input buffer byte pointer
  66. OTPTR    DC.L    0        ;Output buffer byte pointer
  67. INBUFF    DS.B    128        ;Input buffer
  68. INBUFFE    EQU    @        ;End of input buffer +1
  69. OTBUFF    DS.B    128        ;Output buffer
  70. OTBUFFE    EQU    @        ;End of output buffer +1
  71. INCNT    DC.B    0        ;Input block counter
  72. OTCNT    DC.B    0        ;Output block counter
  73. ACKNAK    DC.B    0        ;Contains an ACK or a NAK
  74.  
  75. ;-----------------------------------------------------------------------
  76. ;Open (initialize) the RS-232 port and set the input buffer to empty
  77. ;
  78. OPENI    MOVE.L    D0,-(SP)    ;Save D0
  79.  
  80.     MOVE.B    #1,INCNT.L    ;Init block counter
  81.     MOVE.L    #INBUFFE,INPTR.L ;Set pointer to empty
  82.     CLR.B    EOTFLG        ;Init EOT flag
  83.     BSR    OPEN        ;Init RS-232 port for I/O, set baud rate
  84.  
  85.     MOVEQ    #ACK,D0        ;Kludge to avoid CLOSEI case (Wayne
  86.     BSR    BYTEOUT        ; was write). Send ACK to tell send
  87.                 ; CLOSE routine we're done
  88.     BSR    FLUSH        ;Flush out any bytes in the receive reg
  89.     MOVE.B    #NAK,ACKNAK.L    ;Initially send a NAK
  90.  
  91.     MOVE.L    (SP)+,D0    ;Restore D0
  92. DUMMY    RTS
  93.  
  94. ;-----------------------------------------------------------------------
  95. ;Open (initialize) the RS-232 port and set the output buffer to empty
  96. ;
  97. OPENO    MOVE.L    D0,-(SP)    ;Save D0
  98.  
  99.     MOVE.B    #1,OTCNT.L    ;Init block counter
  100.     MOVE.L    #OTBUFF,OTPTR.L    ;Set pointer to empty
  101.  
  102.     BSR    OPEN        ;Init RS-232 port for I/O, set baud rate
  103.     BSR    FLUSH        ;Flush out any bytes in the receive reg
  104.  
  105. OPO10    BSR    BYTEIN        ;Wait for a NAK
  106.     BEQ.S    OPO10        ;Wait forever (i.e. no timeout)
  107.     CMPI.B    #NAK,D0        ;This is required because SENDBUF must
  108.     BNE.S    OPO10        ; wait for an ACK or NAK at its end
  109.  
  110.     MOVE.L    (SP)+,D0    ;Restore D0
  111.     RTS
  112.  
  113. ;-----------------------------------------------------------------------
  114. ;Get a character from the buffer and return it in D0.
  115. ; If the buffer is empty then receive another one.
  116. ;
  117. CHIN    MOVE.L    A6,-(SP)    ;Save A6
  118.  
  119.     MOVEA.L    INPTR.L,A6    ;Get the buffer pointer
  120.     CMPA.L    #INBUFFE,A6    ;Is the buffer empty?
  121.     BLO.S    CHI10        ;Branch if not
  122.     BSR    GETBUF        ;Receive another buffer
  123.     TST.L    ERRLOC        ;Did we get an error?
  124.     BNE.S    CHI90        ;Branch if yes, exit
  125.     MOVEA.L    #INBUFF,A6    ;Reset byte pointer to start of buffer
  126.  
  127. CHI10    MOVEQ    #0,D0
  128.     MOVE.B    (A6)+,D0    ;Get the byte, and increment pointer
  129.     MOVE.L    A6,INPTR.L    ;Save the pointer
  130.  
  131. CHI90    MOVEA.L    (SP)+,A6    ;Restore A6
  132.     RTS
  133.  
  134. ;-----------------------------------------------------------------------
  135. ;Output the byte in D0 to the buffered RS-232 port
  136. ;
  137. CHOUT    MOVE.L    A6,-(SP)    ;Save A6
  138.  
  139.     MOVEA.L    OTPTR.L,A6    ;Is there room in the buffer for this
  140.     CMPA.L    #OTBUFFE,A6    ; byte?  i.e. is OTPTR < OTBUFFE?
  141.     BLO.S    CHO50        ;Branch if yes
  142.  
  143.     BSR    SENDBUF        ;The buffer is full so send it
  144.     TST.L    ERRLOC        ;Did we get an error?
  145.     BNE.S    CHO90        ;Branch if yes, exit
  146.     MOVEA.L    #OTBUFF,A6    ;Reset byte pointer to start of output
  147.                 ; buffer
  148. CHO50    MOVE.B    D0,(A6)+    ;Store the byte in the output buffer
  149.     MOVE.L    A6,OTPTR.L    ; bump pointer and save it
  150.  
  151. CHO90    MOVEA.L    (SP)+,A6    ;Restore A6
  152.     RTS
  153.  
  154. ;-----------------------------------------------------------------------
  155. ;Close the output file
  156. ;
  157. CLOSE    MOVE.L    D0,-(SP)    ;Save D0
  158.  
  159.     MOVEQ    #EOF,D0        ;Append an EOF to the end of file
  160.     BSR    CHOUT
  161.  
  162.     MOVE.L    OTPTR.L,D0    ;Is there anything in the output buffer?
  163.     CMPI.L    #OTBUFF,D0
  164.     BEQ.S    CL20        ;Branch if not
  165.     BSR    SENDBUF        ;Send the output buffer
  166. CL20
  167.     MOVEQ    #EOT,D0        ;Send an EOT
  168.     BSR    BYTEOUT
  169.  
  170. CL90    MOVE.L    (SP)+,D0    ;Restore D0
  171.     RTS
  172.  
  173. ;-----------------------------------------------------------------------
  174. ;Return the address of the information array in D0
  175. ;
  176. GETINFO    MOVE.L    #INFO,D0
  177.     RTS
  178.  
  179. INFO    DC.L    START        ;Start and end addresses of this handler
  180.     DC.L    END
  181.     DC.L    INFOSTR        ;Description
  182. INFOSTR    ASCII    'XMODEMA   DEC-04-89  RS-232 handler, 19200 baud'
  183.     DC.B    0
  184.  
  185. ;=======================================================================
  186. ;SUBROUTINES
  187. ;
  188. ;Receive a buffer of data from the RS-232 port
  189. ;Register usage:
  190. ;    D0 - I/O
  191. ;    D1 - Scratch and loop counter
  192. ;    D2 - Checksum
  193. ;    A6 - Buffer pointer
  194. ;
  195. GETBUF    MOVEM.L    D0-D2/A6,-(SP)    ;Save registers
  196.  
  197. GET00    MOVE.B    ACKNAK.L,D0    ;Send handshake character (ACK or NAK)
  198.     BSR    BYTEOUT        ; to indicate we're ready to receive
  199.     MOVE.B    #NAK,ACKNAK.L    ;Assume the transmission will fail
  200.  
  201. GET10    BSR    BYTEIN        ;Read the first byte of the block
  202.     BEQ.S    GET00        ;Send a NAK if we time out
  203.     CMPI.B    #EOT,D0        ;Is it an EOT?
  204.     BNE.S    GET15        ;Branch if not
  205.     ST    EOTFLG        ;Set EOT flag
  206.     BRA    GET90        ; and exit
  207. GET15
  208.     CMPI.B    #SOH,D0        ;Loop until we get the start-of-heading
  209.     BNE.S    GET10
  210.  
  211.     BSR    BYTEIN        ;Get the block number
  212.     BEQ.S    GET00        ;Send a NAK if we time out
  213.     MOVE.B    D0,D1
  214.     BSR    BYTEIN        ;Get the complement of the block number
  215.     BEQ.S    GET00        ;Send a NAK if we time out
  216.     NOT.B    D0
  217.     CMP.B    D1,D0
  218.     BNE.S    GET00        ;NAK if they don't match
  219.  
  220.     CMP.B    INCNT.L,D0    ;Is this the block we expect?
  221.     BEQ.S    GET20        ;Branch if yes
  222.  
  223.     ADDQ.B    #1,D0        ;Is this the previous block?
  224.     CMP.B    INCNT.L,D0
  225.     BEQ.S    GET17        ;Branch if yes
  226.  
  227.     JSR    VERROR        ;Else fatal error
  228.     ASCII    '402 - BAD XMODEM BLOCK NO.'
  229.     DC.B    0
  230.     BRA.S    GET90        ;Exit
  231. GET17
  232.     MOVE.W    #127+1,D1    ;Init counter for 128 bytes + checksum
  233. GET18    BSR    BYTEIN        ;Get the byte
  234.     BEQ.S    GET00        ;Send a NAK if we time out, & start over
  235.     DBF    D1,GET18    ;Loop for 128 bytes
  236.     MOVE.B    #ACK,ACKNAK.L    ;Indicate no errors
  237.     BRA    GET00        ;Get next block
  238. GET20
  239.     MOVE.W    #127,D1        ;Init loop counter to receive 128 bytes
  240.     MOVEA.L    #INBUFF,A6    ;Point to start of output buffer
  241.     CLR.B    D2        ;Init checksum
  242. GET50    BSR    BYTEIN        ;Get the byte
  243.     BEQ    GET00        ;Send a NAK if we time out, & start over
  244.     ADD.B    D0,D2        ;Update checksum
  245.     MOVE.B    D0,(A6)+    ;Store byte into input buffer
  246.     DBF    D1,GET50    ;Loop for 128 bytes
  247.  
  248.     BSR    BYTEIN        ;Read in the checksum
  249.     BEQ    GET00        ;Send a NAK if we time out, & start over
  250.     CMP.B    D2,D0        ;Compare it to what we computed
  251.     BNE    GET00        ;Branch if we failed
  252.     MOVE.B    #ACK,ACKNAK.L    ;Otherwise, indicate no errors and exit
  253.     ADDQ.B    #1,INCNT.L    ;Count this block
  254.  
  255. GET90    MOVEM.L    (SP)+,D0-D2/A6    ;Restore registers
  256.     RTS
  257.  
  258. ;-----------------------------------------------------------------------
  259. ;Send the output buffer out the RS-232 port
  260. ;
  261. SENDBUF    MOVEM.L    D0-D2/A6,-(SP)    ;Save registers
  262.  
  263. SEND00    MOVEQ    #SOH,D0        ;Send the start-of-heading byte
  264.     BSR    BYTEOUT
  265.  
  266.     BSR    FLUSH        ;Flush out any bytes in the receive reg
  267.  
  268.     MOVE.B    OTCNT.L,D0    ;Send the block count
  269.     BSR    BYTEOUT
  270.     NOT.B    D0        ;Send its complement
  271.     BSR    BYTEOUT
  272.  
  273.     MOVE.W    #127,D1        ;Init loop counter to send 128 bytes
  274.     MOVEA.L    #OTBUFF,A6    ;Point to start of output buffer
  275.     CLR.B    D2        ;Init checksum
  276. SEND10    MOVE.B    (A6)+,D0    ;Get byte from output buffer
  277.     ADD.B    D0,D2        ;Update checksum
  278.     BSR    BYTEOUT        ;Send the byte
  279.     DBF    D1,SEND10    ;Loop for 128 bytes
  280.  
  281.     MOVE.B    D2,D0        ;Send the checksum
  282.     BSR    BYTEOUT
  283.  
  284. SEND20    BSR    BYTEIN        ;Wait for and get response
  285.     BEQ.S    SEND20        ;Wait forever -- no timeout
  286.     CMPI.B    #ACK,D0        ;If it is not an ACK then re-send
  287.     BNE.S    SEND00
  288.     ADDQ.B    #1,OTCNT.L    ;Count this block
  289.  
  290.     MOVEM.L    (SP)+,D0-D2/A6    ;Restore registers
  291.     RTS
  292.  
  293. ;======================================================================
  294. ;LOWEST LEVEL I/O ROUTINES:
  295. ;
  296. SERDATR    EQU    $DFF018        ;Serial port data and status register
  297. SERDAT    EQU    $DFF030        ;Serial port data register (write)
  298. SERPER    EQU    $DFF032        ;Serial port period and control register
  299. INTREQ    EQU    $DFF09C        ;Interrupt request (status) bits
  300.  
  301. ;----------------------------------------------------------------------
  302. ;Routine to initialize RS-232 I/O.
  303. ;
  304. OPEN    MOVE.W    #185,SERPER.L    ;Set 8 bits, 19200 baud
  305.     MOVE.W    #$0800,INTREQ.L    ;Clear "receive buffer full" status bit
  306.     RTS
  307.  
  308. ;-----------------------------------------------------------------------
  309. ;Flush out any characters in the receive register.
  310. ;
  311. FLUSH    MOVE.L    D0,-(SP)    ;Save D0
  312.  
  313. FL10    MOVE.W    SERDATR.L,D0    ;Read status and data
  314.     BTST    #14,D0        ;Receive buffer full
  315.     BEQ.S    FL90        ;Branch if not -- exit
  316.  
  317.     MOVE.W    #$0800,INTREQ.L    ;Clear "receive buffer full" status bit
  318.     BRA.S    FL10        ;Loop until all bytes flushed
  319.  
  320. FL90    MOVE.L    (SP)+,D0    ;Restore D0
  321.     RTS
  322.  
  323. ;-----------------------------------------------------------------------
  324. ;Get a byte from the RS-232 port and return it in D0.
  325. ; "EQ" status is returned if a timeout occurs.
  326. ;
  327. BYTEIN    MOVEM.L    D1,-(SP)    ;Save D1
  328.  
  329.     MOVEQ    #20,D1        ;Init timeout counter for about 10 seconds
  330.     SWAP    D1        ;(Trick to save one whole word!)
  331.  
  332. BYIN10    MOVE.W    SERDATR.L,D0    ;Read status and data
  333.     BTST    #14,D0        ;Receive buffer full
  334.     BNE.S    BYIN30        ;Branch if yes -- go read byte
  335.  
  336.     SUBQ.L    #1,D1        ;Else tick off the timeout counter
  337.     BNE.S    BYIN10        ;Loop back if there is still time left
  338.  
  339.     MOVEM.L    (SP)+,D1    ;Restore D1 without changing status
  340.     RTS            ;Timed out -- return with "EQ" status
  341.  
  342. BYIN30    MOVE.W    #$0800,INTREQ.L    ;Clear "receive buffer full" status bit
  343.     ANDI.L    #$000000FF,D0    ;Return byte in D0
  344.     ANDI.B    #$FB,CCR    ;Make sure Z flag is clear (NE)
  345.  
  346.     MOVEM.L    (SP)+,D1    ;Restore D1 without changing status
  347.     RTS
  348.  
  349. ;----------------------------------------------------------------------
  350. ;Routine to output the byte in D0 to an RS-232 port.
  351. ;
  352. BYTEOUT    MOVEM.W    D0-D1,-(SP)    ;Save low word of registers
  353.  
  354. BYO10    MOVE.W    SERDATR.L,D1    ;Get serial port status
  355.     BTST    #13,D1        ;Test transmitter buffer empty
  356.     BEQ.S    BYO10        ;Branch if it is not empty
  357.     ANDI.W    #$00FF,D0    ;Add start and stop bits to the
  358.     ORI.W    #$0100,D0    ; data byte
  359.     MOVE.W    D0,SERDAT.L    ;Output data
  360.  
  361.     MOVEM.W    (SP)+,D0-D1    ;Restore registers
  362.     RTS
  363.  
  364.  
  365. END    EQU    @-1        ;Address where this handler ends
  366.     IF    END > $7EF00
  367. ;    IF    END > START +$400
  368.     ERROR -- FILE IS TOO LONG
  369.     ENDIF
  370.  
  371. ;-----------------------------------------------------------------------
  372. ;Hook this handler into the device handler table
  373. ;
  374.     ORG    4 *DEVNUM +DEVTBL
  375.     DC.L    XMODEM
  376.  
  377.     END
  378. --------------------------
  379. ;Hook this handler into the device handler table
  380. ;
  381.     ORG    4 *DEVNUM +DEVTBL
  382.     DC